/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.common.config;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONCreator;
import com.google.common.collect.Sets;
import info.peigen.hotpot.common.core.enums.Messageable;
import info.peigen.hotpot.common.core.lang.money.Money;
import org.noear.snack.ONode;
import org.noear.snack.core.Feature;
import org.noear.snack.core.Options;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
import org.noear.solon.serialization.snack3.SnackActionExecutor;
import org.noear.solon.serialization.snack3.SnackRenderFactory;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.List;
import java.util.Set;

/**
 * <b>(JsonSerializationConfig)</b>
 *
 * @author Lihai
 * @version 1.0.0
 * @since 2023/8/18
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@Configuration
public class JsonSerializationConfig {

    @Bean
    public void jsonInit(@Inject SnackRenderFactory factory, @Inject SnackActionExecutor executor) {
        // Enum
        Set<Class<?>> classes            = Sets.newHashSet();
        Set<Class<?>> hotpotEnumsClasses = ClassScanner.scanPackageBySuper("info.peigen.hotpot", Messageable.class);
        classes.addAll(hotpotEnumsClasses);

        // 为Project工程里面的枚举增加扫描
        String enumPackage = Solon.context().cfg().get("hotpot.admin.enumPackage");
        if (StrUtil.isEmpty(enumPackage)) {
            enumPackage = Solon.context().cfg().get("hotpot.manage.enumPackage");
        }
        if (StrUtil.isNotEmpty(enumPackage)) {
            Set<Class<?>> projectEnumsClasses = ClassScanner.scanPackageBySuper(enumPackage, Messageable.class);
            classes.addAll(projectEnumsClasses);
        }

        classes.stream().filter(Class::isEnum).forEach(each -> {
            addEncoder(each, factory.config());
            addDecoder(each, executor.config());
        });

        // Money
        factory.addEncoder(Money.class, (data, node) -> {
            String jsonString = JSON.toJSONString(data);
            ONode  oNode      = ONode.loadStr(jsonString, Feature.StringJsonToNode);
            node.val(oNode);
        });
        executor.config().addDecoder(Money.class, (data, node) -> JSON.parseObject(data.toJson(), Money.class));

    }

    public void addEncoder(Class<?> clz, Options options) {
        options.addEncoder(clz, (data, node) -> {
            String jsonString = toJsonString(data, clz);
            ONode  oNode      = ONode.loadStr(jsonString, Feature.StringJsonToNode);
            node.val(oNode);
        });
    }

    public void addDecoder(Class<?> clz, Options options) {
        options.addDecoder(clz, (node, type) -> {
            Method[] methods = ReflectUtil.getMethods(clz, method -> AnnotationUtil.hasAnnotation(method, JSONCreator.class));
            if (!node.contains("code") || StrUtil.isEmpty(node.get("code").val().toString())) {
                String code = StrUtil.replace(node.val().toString(), "\"", "");
                if (StrUtil.isEmpty(code)) {
                    return null;
                }
                Method getByCode = ReflectUtil.getPublicMethod(clz, "getByCode", String.class);
                return ReflectUtil.invokeStatic(getByCode, code);
            }

            // 配置了@JSONCreate 直接调方法
            if (ArrayUtil.isNotEmpty(methods)) {
                Method method = methods[0];
                return ReflectUtil.invokeStatic(method, node.get("code").val());
            }
            // 未配置，调约定getByCode方法
            Method getByCode = ReflectUtil.getPublicMethod(clz, "getByCode", String.class);
            return ReflectUtil.invokeStatic(getByCode, node.contains("code") ? node.get("code").val() : node.val());
        });
    }

    private String toJsonString(Object data, Class clz) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("{");
        // 获取枚举字段
        List<String> fieldNames = EnumUtil.getFieldNames(clz);
        // 拼接json字符串
        fieldNames.stream()
                .filter(fileName -> !StrUtil.equals(fileName, "name"))
                .filter(fileName -> !StrUtil.equals(fileName, "hash"))
                .forEach(fieldName -> {
                    String format = MessageFormat.format("\"{0}\":\"{1}\",", fieldName, ReflectUtil.getFieldValue(data, fieldName));
                    buffer.append(format);
                });
        buffer.append("}");
        return StrUtil.replaceLast(buffer.toString(), ",", "");
    }
}